#include "raid.h"
#include "logfile.h"

int main (void)
{
	pthread_mutex_init( &mutexPedidos, NULL);
	pthread_mutex_init( &mutexPedidosSincro, NULL);
	pthread_mutex_init( &mutexAtendidos, NULL);
	pthread_mutex_init( &mutexDiscos, NULL);
	pthread_mutex_init( &mutexSincro, NULL);
	pthread_mutex_init( &mutexSockFS, NULL);
	sem_init( &semPedSincro,0,0 );
	sem_init( &semPed,0,0 );
	int32_t sockGral, sockPD, rc, sock, t_id , newSockGral, intVerif, buscaSocket, fdmax, salida, sinFS;
	Paquete* paquete;
	char*  bufferEntrada = (char*) malloc (sizeof(char)*BUFF_LIMIT);
	char*  bufferSalida;
	char*  stringLog = (char*) malloc (sizeof(char)*128);
	fd_set master;
	fd_set read_fds;

	FD_ZERO(&master);
	FD_ZERO(&read_fds);
	raidFuncionando = 1;
	salida = 0;
	fdmax = 0;
	listaDiscos = NULL;
	listaAtendidos = NULL;
	listaPedidos = NULL;
	listaPedidosLast = NULL;
	listaPedidosSincro = NULL;
	listaPedidosSincroLast = NULL;
	listaSincro = NULL;
	listaPUTwait = NULL;
	sock = 0;
	t_id = 0;
	sinPD = 1;
	sinFS = 1;
	sockGral = initSockGral();
	if ( sockGral < 0 )
	{
		printf("No se pudo crear el socket General\n");
		return 0;
	}
	FD_SET(sockGral, &master);
	if (sockGral > fdmax)
	    fdmax = sockGral;
	while(!salida)
	{
	    read_fds = master; /* Copio la lista principal de file descriptor a la temporal	*/
	    if (select(fdmax+1, &read_fds, NULL, NULL, 0) == -1)
	    {
	    	printf("Error: Hubo un error al seleccionar la conexion\n");
	    }
	    /* Recorro las conexiones activas en busca de informacion para leer*/
	    for (buscaSocket = 0; buscaSocket <= fdmax + 1; buscaSocket++)
	    {
	        if ( FD_ISSET(buscaSocket, &read_fds))
	        {
	        	/*Busco cual es el socket */
	        	if (buscaSocket == sockGral) /* TRATAMIENTO GENERAL */
	        	{
	        		newSockGral = accept(sockGral,NULL,NULL);
	        		if (newSockGral < 0)
	        		{
	        			printf("Accept general error\n");
	        		}
	        		strcpy(bufferEntrada,"");
	        		rc = recv(newSockGral, bufferEntrada, BUFF_LIMIT, 0);
	        		if (rc < 0)
	        			printf("Read error nueva conexion\n");
	        		paquete = DesSerializar(bufferEntrada);
	        		/* ES HANDSHAKE DE FILE SYSTEM ??? */
	        		if ( paquete->Tipo == HANDSHAKE && paquete->Length == 0 && sinFS)
	        		{
	        			if(!sinPD)
	        			{
	        				if(sinFS)
	        				{
	        					if( raidFuncionando )
	        					{
	        						sockFS = newSockGral;
	        						intVerif = responderHandshake (sockFS );
	        						if (intVerif != -1)
	        						{
	        							FD_SET(sockFS, &master);
	        							if (sockFS > fdmax)
	        								fdmax = sockFS;
	        							strcpy( stringLog, "PRAID Online!");
	        							escribir_logfile(NOMPS, (long int)syscall(224), INFO, stringLog );
	        							sinFS = 0;
	        						}
	        						else
	        							printf("Error en el handshake del File System\n");
	        					}
	        					else /* si no anda el raid */
	        					{
	        						printf("Proceso raid Destruido\n");
	        						paquete->Tipo = CLOSE_CONECTION;
	        						bufferSalida = Serializar(paquete);
	        						send(newSockGral, bufferSalida,535,0);
	        						free( bufferSalida );
	        						close(newSockGral);
	        					}
	        				}
	        				else /* si ya hay otro PFS conectado */
	        				{
	        					printf("Un nuevo filesystem tratando de conectarse: ACCESO DENEGADO\n");
	        					paquete->Tipo = CLOSE_CONECTION;
	        					bufferSalida = Serializar(paquete);
	        					send(newSockGral,Serializar(paquete),535,0);
	        					free( bufferSalida );
	        					close(newSockGral);
	        				}
	        			}
	        			else /* si no hay PPD */
	        			{
	        				printf("No hay discos conectados\n");
	        				paquete->Tipo = CLOSE_CONECTION;
	        				pthread_mutex_lock(&mutexSockFS);
	        				bufferSalida = Serializar(paquete);
	        				send(sockFS, bufferSalida,535,0);
	        				free( bufferSalida );
	        				close(sockFS);
	        				pthread_mutex_unlock(&mutexSockFS);
	        			}
	        		}
	        		/* ES HANDSHAKE DE ALGUN DISCO ??? */
	        		if ( paquete->Tipo == HANDSHAKE && paquete->Length > 0 )
	        		{
	        			sockPD = newSockGral;
	        			pthread_mutex_lock( &mutexDiscos );
	        			intVerif = !otroDiscoConMismoNombre(paquete->Datos);
						pthread_mutex_unlock( &mutexDiscos );
						intVerif = 1;
						paquete->Datos[paquete->Length] = '\0';
	        			if ( intVerif )
	        			{
	        				intVerif = responderHandshake (sockPD);
	        				if (intVerif != -1)
	        				{
	        					pthread_mutex_lock( &mutexDiscos );
	        					agregarDisco( sockPD, paquete); /* agrega disco a la lista */
	        					pthread_mutex_unlock ( &mutexDiscos );
	        					strcpy( stringLog, "Se conecto el disco: ");
	        					strcat( stringLog, (char*) paquete->Datos );
	        					escribir_logfile(NOMPS, (long int)syscall(224), INFO, stringLog );
	        					pthread_t threadID;
	        					packThread* packTh = malloc (rc);
	        					packTh->sock = sockPD;
	        					packTh->cantSectores = paquete->Sector;
	        					rc = pthread_create(&threadID, NULL, nuevoThreadDisco, (void*) packTh);
	        					if (rc)
	        					{
	        						printf("ERROR; return code from pthread_create() is %d\n", rc);
	        					}
	        					if( sinPD == 1 )
	        						printf("%s Disco maestro conectado\n",paquete->Datos);
	        					else
	        						printf("%s Conectado esperando sincronizacion\n",paquete->Datos);
	        					sinPD = 0;
	        				}
	        			}
	        			else
	        			{
	        				printf("Disco ya conectado, ACCESO DENEGADO\n");
	        				paquete->Tipo = CLOSE_CONECTION;
	        				bufferSalida = Serializar(paquete);
	        				send(sockPD, bufferSalida ,535,0);
	        				free( bufferSalida );
	        				close(sockPD);
	        			}

	        		}
	        	}
	        	/* si es un pedido del Proceso File System */
	        	if (buscaSocket == sockFS)
	        	{
	        		strcpy(bufferEntrada,"");
	        		rc = recv(sockFS, bufferEntrada, BUFF_LIMIT,0);
	        		if (rc < 0)
	        		{
	        			printf("Se desconecto el Proceso File System\n");
	        			close(sockFS);
	        			FD_CLR(sockFS,&master);
	        		}
	        		paquete = DesSerializar(bufferEntrada);
	        		if ( paquete->Tipo == GET_SECTOR )
	        		{
	        			sockPD = proximoDisco();
	        			if ( sockPD == -1)
	        			{
	        				printf("Sistema RAID Destruido\n");
	        				/* enviar info a los dispositivos para terminar */
	        			}
	        			strcpy( stringLog, "Peticion de lectura enviando al disco: ");
	        			strcat( stringLog, nombreDisco(sockPD) );
	        			escribir_logfile(NOMPS, (long int)syscall(224), INFO, stringLog );
	        			printf("Pedido de lectura para el disco %s del sector %d\n",nombreDisco(sockPD),paquete->Sector );
	        			pthread_mutex_lock( &mutexPedidos );
	        			nuevoPedido( sockPD, paquete ); /* agrega un pedido a la lista de pedidos*/
	        			sem_post( &semPed );
	        			pthread_mutex_unlock( &mutexPedidos );
	        		}
	        		if ( paquete->Tipo == PUT_SECTOR )
	        		{
	        			strcpy( stringLog, "Peticion de escritura enviando a todos los discos del sector: ");
	        			strcat( stringLog, (char*) paquete->Sector );
	        			escribir_logfile(NOMPS, (long int)syscall(224), INFO, stringLog );
	        			printf("Escribiendo en todos los discos el sector %d\n",paquete->Sector);
	        			pthread_mutex_lock( &mutexPedidos );
	        			replicarPedido(paquete);
	        			pthread_mutex_unlock( &mutexPedidos );
	        		}
	        	}
	    	}/* Termina el IF de tratamientos */
	    } /* Termina el For de tratamientos */
	} /* Termina el WHILE del select */
return 0;
}
